/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package JeuDeLaVie;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.border.Border;

/**
 * Vue du modèle MVC.
 *
 * @author
 */
public abstract class Fenetre implements EnvListener
{
    //=====================
    // Les objets affichés
    //=====================
    protected JFrame frame;       //Fenêtre du programme
    protected JPanel principal;   //Panneau qui accueillera tous les composants
    protected JPanel barreBoutons;   //Panneau des boutons
    protected JPanel panContr;    //Panneau qui accueillera les différents
    protected FenetreAide fenAide;
    
    //---------------------
    //panneaux de contrôle
    //---------------------
    protected JButton boutonLancer; //Bouton pour lancer la simulation
    protected JButton boutonIni;  //Bouton qui vide la grille, met tout à "mort"
    protected JButton boutonAlea; //Bouton qui remplit les cases de la grille de manière aléatoire
    
    //--------
    // Motifs
    //--------
    protected JPanel panneauMotif;
    protected JButton boutonMotif;
    protected JComboBox listeMotifs;
    
    //------------------
    // Choix des Regles
    //------------------
    protected JPanel panneauRegles;
    protected JComboBox listeRegles;
    protected JLabel reglesEnCours;
    protected JLabel explicationsRegles;
    protected JButton choixRegles;
    
    //----------------
    // barre de menus
    //----------------
    protected JMenuBar menu;
    protected JMenu mJeu;
    protected JMenuItem mJeuAide;
    protected JMenuItem mLancer;
    protected JMenuItem mIni;
    protected JMenuItem mAlea;
    
    protected JMenuItem mSauver;
    protected JMenuItem mCharger;
    
    //--------------------------------------
    // Les liens vers les autres structures
    //--------------------------------------
    protected Environnement lienEnv;  //exigée par la consigne : on va se servir directement dans l'env
    protected Controleur lienContr;

    //======================================================================================================================
    // Les méthodes 
    //==============
    /**
     * Constructeur de Fenêtre : crée la fenêtre, établit les liens vers le
     * contrôleur et le modèle
     *
     * @param env Environnement auquel lier la fenêtre
     * @param contr Contrôleur auquel lier la fenêtre
     */
    public Fenetre(Environnement env, Controleur contr)
    {
        super();

        // on fait le lien vers le contrôleur
        lienEnv = env;
        lienContr = contr;

        int nb = 75;
        //valeurs choisies arbitrairement, mais qu'on peut changer aisément
        construireFenetre(nb, nb);

        // Met fin au programme quand on ferme la fenêtre principale
        frame.addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent arg0)
            {
                lienContr.fermerProgramme(arg0);
            }
        });

    }

    /**
     * Méthode qui crée la fenêtre à partir de la taille de la grille voulue.
     * Crée la frame principale et tous ses éléments, génère la grille
     *
     * @param nbLargeur la largeur de la grille
     * @param nbHauteur la hauteur de la grille
     */
    private void construireFenetre(int nbLargeur, int nbHauteur)
    {
        //Panneau principal
        frame = new JFrame();
        frame.setTitle("Jeu de la vie");
        frame.setSize(nbLargeur * 10 + 200, nbHauteur * 10);

        // Une bordure noire classique
        Border bordureNoire = BorderFactory.createLineBorder(Color.BLACK, 1);
        
        //----------------------------------------------------------
        // Création des ActionsListeners qui vont être nécessaires
        //----------------------------------------------------------

        // Remplissage aélatoire de la grille
        ActionListener listenerAlea = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedAlea();
            }
        };

        // Réinitialisation de la grille
        ActionListener listenerReini = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedInit();
            }
        };

        // Lance la simulation
        ActionListener listenerLancer = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedLancer();
            }
        };
                
        fenAide = new FenetreAide(frame);
        ActionListener listenerAide = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedAide();
            }
        };
        
        ActionListener listenerMotif = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedMotif(listeMotifs.getSelectedIndex());
            }
        };
        
        ActionListener listenerRegles = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedChoixRegles(listeRegles.getSelectedIndex());
            }
        };
        
        ActionListener listenerBoutonChoixRegles = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.actionPerformedBoutonChoixRegles(listeRegles.getSelectedIndex());
            }
        };

        // Charge la version sauvegardée de la grille
        ActionListener listenerCharger = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.chargerSauvegarde();
            }
        };

        // Sauvegarde la grille
        ActionListener listenerSauvegarder = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                lienContr.enregistrerSauvegarde();
            }
        };

        //--------------------------------------------------------------
        // Eventuelle barre de menu (passque pouvoir faire la même chose
        // de plein de façons différentes, ça serait bien ^^
        //--------------------------------------------------------------
        menu = new JMenuBar();
        mJeu = new JMenu("Jeu");
        mAlea = new JMenuItem("Nouveau tirage aléatoire");
        mIni = new JMenuItem("Réinitialiser la grille");
        mLancer = new JMenuItem("Lancer la simulation");
        mJeuAide = new JMenuItem("Aide");
        mCharger = new JMenuItem("Charger la dernière sauvegarde");
        mSauver = new JMenuItem("Sauvegarder l'état actuel");
        
        menu.add(mJeu);
        mJeu.add(mCharger);
        mJeu.add(mSauver);
        mJeu.add(mAlea);
        mJeu.add(mIni);
        mJeu.add(mLancer);
        mJeu.add(mJeuAide);
        frame.setJMenuBar(menu);

        //---------------------------
        // Mise sur écoute des menus
        //---------------------------
        mAlea.addActionListener(listenerAlea);
        mIni.addActionListener(listenerReini);
        mJeuAide.addActionListener(listenerAide);
        mLancer.addActionListener(listenerLancer);
        mSauver.addActionListener(listenerSauvegarder);
        mCharger.addActionListener(listenerCharger);

        //--------------------
        // Raccourcis clavier
        //--------------------
        //Tirage aléatoire de grille
        KeyStroke f5 = KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0);
        mAlea.setAccelerator(f5);

        //Réinitialisation de grille
        KeyStroke f2 = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
        mIni.setAccelerator(f2);
        
        //Affichage de l'aide
        KeyStroke f1 = KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0);
        mJeuAide.setAccelerator(f1);
        
        //Mettre en pause / Lancer la simulation
        KeyStroke f3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0);
        mLancer.setAccelerator(f3);
        
        //Sauver
        KeyStroke f4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0);
        mSauver.setAccelerator(f4);
        
        //Charger
        KeyStroke f9 = KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0);
        mCharger.setAccelerator(f9);

        //-----------------------
        //Panneau pour la grille
        //-----------------------
        principal = new JPanel(new GridLayout(nbLargeur, nbHauteur, 0, 0));

        //--------------------
        // On trace la grille
        //--------------------
        construireGrille(nbLargeur, nbHauteur);

        //--------------
        //Panneau Motif
        //--------------
        panneauMotif = new JPanel();
        Dimension d = new Dimension(200, 20);
        panneauMotif.setLayout(new BoxLayout(panneauMotif, BoxLayout.Y_AXIS));
        boutonMotif = new JButton("Placer motif");
        
        //On crée la liste des motifs
        Object[] elementsListe = new Object[]{"Petit planeur 3x3", "Croix (oscillateur) 8x8", "Petit Canon 36x9"};
        listeMotifs = new JComboBox<>(elementsListe);
        listeMotifs.setPreferredSize(d);
        listeMotifs.setMinimumSize(d);
        listeMotifs.setMaximumSize(d);

        panneauMotif.add(Box.createVerticalStrut(10));
        boutonMotif.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauMotif.add(boutonMotif);
        panneauMotif.add(Box.createVerticalStrut(10));
        listeMotifs.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauMotif.add(listeMotifs);
        panneauMotif.add(Box.createVerticalStrut(10));
        
        panneauMotif.setBorder(bordureNoire);
        
        //---------------
        //Panneau Règles
        //---------------
        panneauRegles = new JPanel();
        panneauRegles.setLayout(new BoxLayout(panneauRegles, BoxLayout.Y_AXIS));
        
        //On crée la liste des motifs
        Object[] elementsListeRegles = new Object[]{"Règles de base", "Règles de test", "Règles pondérées"};
        listeRegles = new JComboBox<>(elementsListeRegles);
        listeRegles.setPreferredSize(d);
        listeRegles.setMinimumSize(d);
        listeRegles.setMaximumSize(d);
        
        //On paramètre le bouton de choix des règles
        choixRegles = new JButton("Utiliser ces règles");
        
        // paramétrage des labels (à l'ini : règles de base)
        reglesEnCours = new JLabel("Règles en cours : Règles de base");
        explicationsRegles = new JLabel("<html>Si une cellule est encadrée de deux cellules vivantes, son état ne change pas. <br>Si elle est entourée par trois cellules vivantes, elle devient vivante.<br>Sinon, elle meurt.</html>");
        
        panneauRegles.add(Box.createVerticalStrut(10));
        reglesEnCours.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauRegles.add(reglesEnCours);
        panneauRegles.add(Box.createVerticalStrut(10));
        listeRegles.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauRegles.add(listeRegles);
        panneauRegles.add(Box.createVerticalStrut(10));
        choixRegles.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauRegles.add(choixRegles);
        panneauRegles.add(Box.createVerticalStrut(10));
        explicationsRegles.setAlignmentX(Component.CENTER_ALIGNMENT);
        panneauRegles.add(explicationsRegles);
        panneauRegles.add(Box.createVerticalStrut(10));
        panneauRegles.setBorder(bordureNoire);
        
        
        
        //------------------------------------------------
        //Panneau de boutons, avec les boutons en colonne
        //------------------------------------------------
        barreBoutons = new JPanel();
        barreBoutons.setLayout(new BoxLayout(barreBoutons, BoxLayout.Y_AXIS));

        boutonIni = new JButton("Réinitialiser");
        boutonAlea = new JButton("Grille aléatoire");
        boutonLancer = new JButton("Lancer");
        barreBoutons.add(Box.createVerticalStrut(10));
        boutonIni.setAlignmentX(Component.CENTER_ALIGNMENT);
        barreBoutons.add(boutonIni);
        barreBoutons.add(Box.createVerticalStrut(10));
        boutonAlea.setAlignmentX(Component.CENTER_ALIGNMENT);
        barreBoutons.add(boutonAlea);
        barreBoutons.add(Box.createVerticalStrut(10));
        boutonLancer.setAlignmentX(Component.CENTER_ALIGNMENT);
        barreBoutons.add(boutonLancer);
        barreBoutons.add(Box.createVerticalStrut(10));
        
        barreBoutons.setBorder(bordureNoire);
        
        //-------------------------------
        // On met les boutons sur écoute
        //-------------------------------
        boutonAlea.addActionListener(listenerAlea);
        boutonIni.addActionListener(listenerReini);
        boutonMotif.addActionListener(listenerMotif);
        boutonLancer.addActionListener(listenerLancer);
        choixRegles.addActionListener(listenerBoutonChoixRegles);
        
        // On met le choix des règles sur écoute
        listeRegles.addActionListener(listenerRegles);

        //--------------------
        //Panneau de contrôle
        //--------------------
        panContr = new JPanel();
        panContr.setLayout(new BoxLayout(panContr, BoxLayout.PAGE_AXIS));
        Dimension dPanContr = new Dimension(240, nbHauteur*10);
        panContr.setPreferredSize(dPanContr);

        //On met tout ça dans le gros panneau de contrôle
        // des fois qu'on voudrait rajouter des panneaux de composants
        // à zoner
        panContr.add(Box.createVerticalGlue());
        barreBoutons.setAlignmentX(Component.CENTER_ALIGNMENT);
        panContr.add(barreBoutons);
        panContr.add(Box.createVerticalStrut(10));
        panneauRegles.setAlignmentX(Component.CENTER_ALIGNMENT);
        panContr.add(panneauRegles);
        panContr.add(Box.createVerticalStrut(10));
        panContr.setAlignmentX(Component.CENTER_ALIGNMENT);
        panContr.add(panneauMotif);
        panContr.add(Box.createVerticalGlue());
        
        frame.add(panContr, BorderLayout.LINE_END);
    }

    /**
     * Crée un lien avec le Contrôleur donné
     *
     * @param contr : le contrôleur avec lequel créer le lien
     */
    public void setLienContr(Controleur contr)
    {
        lienContr = contr;
    }

    /**
     * Petit constructeur de grille : On lui donne le nombre de cases en
     * argument, il construit la grille qui correspond et l'ajoute à la fenêtre
     * principale
     *
     * @param nbLargeur la largeur de la grille
     * @param nbHauteur la hauteur de la grille
     */
    protected abstract void construireGrille(int nbLargeur, int nbHauteur);


    //===========
    // Mutateurs
    //===========
    
    /**
     * Change le texte affiché par le bouton de Motif
     * @param nouvText Le nouveau texte qui sera affiché
     */
    public void setLabelBoutonMotif(String nouvText){
        boutonMotif.setText(nouvText);
    }
    
    public void setLabelBoutonMenuLancer(String nouvTextBouton, String nouvTextMenu){
        boutonLancer.setText(nouvTextBouton);
        mLancer.setText(nouvTextMenu);
    }
    
    public void setLabelNomRegles(String nomRegles){
        this.reglesEnCours.setText(nomRegles);
    }
    
    public void setLabelExplRegles(String explRegles)
    {
        explicationsRegles.setText(explRegles);
    }

    //===============
    // Affichage
    //===============
    /**
     * Rend la fenêtre principale du programme visible
     */
    public void affiche()
    {
        frame.setVisible(true);
        this.majGrille();
    }


    /**
     * Cache ou fait apparaître le curseur de la souris
     * @param b Indique s'il faut cacher ou faire apparaître le curseur
     */
    public void cacherCurseur(boolean b){
        if(b){
            //Si on veut cacher le curseur
            // Transparent 16 x 16 pixel cursor image.
            BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);

            // Create a new blank cursor.
            Cursor curseurInvisible = Toolkit.getDefaultToolkit().createCustomCursor(
                    cursorImg, new Point(0, 0), "curseur invisible");
            principal.setCursor(curseurInvisible);
        }
        else{
            principal.setCursor(Cursor.getDefaultCursor());
        }
    }
    
    /**
     * Rend active ou inactive la liste de sélection des motifs plaçables
     * @param b
     */
    public void setChoixMotifActif(boolean b){
        listeMotifs.setEnabled(b);
    }
    
    /**
     * Affiche la fenêtre d'aide
     */
    public void afficherAide(){
        fenAide.setVisible(true);
    }
    
    
    //--------------------------------
    // Méthodes d'affichage absraites
    //--------------------------------
    /**
     * Appelée quand la grille est entièrement modifiée. Met à jour les panneaux
     * graphiques pour correspondre au contenu du modèle (l'environnement). Se
     * base sur une interface au cas où on veuille faire d'autres affichages
     * radicalement différents (qui jouent sur des textfields, des cases à
     * cocher, des labels...)
     */
    @Override
    public abstract void majGrille();

    /**
     * Met à jour l'affichage de la case dont on a passé les coordonnées
     *
     * @param coord Coordonnées de la case à mettre à jour
     */
    @Override
    public abstract void majCase(Point coord);

    /**
     * Affiche autour du motif donné aux coordonnées données
     * la bordure demandée
     * @param motif un motif
     * @param bordure une bordure
     * @param coord une coordonnée
     */
    public abstract void afficheBordsMotif(MotifEnv motif, Border bordure, Point coord);

}
